package ru.alt22.visor.php.ui.core.wizards;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPClassData;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;

import ru.alt22.visor.php.core.reflection.ReflectionUtils;
import ru.alt22.visor.php.ui.swt.OpenPhpClassDialog;
import ru.alt22.visor.php.ui.swt.PHPElementSelector;
import ru.alt22.visor.php.ui.utils.LayoutUtils;

public abstract class NewTypeWizardPage extends WizardPage {
	private static final String TITLE_DESTRUCTOR = "Generate destructor stub";
	private static final String TITLE_INHERITED = "Inherited abstract methods";
	private static final String TITLE_SUPERCLASS_CONSTRCTOR = "Constrctor from superclass";
	private static final String TITLE_CONSTRUCTOR = "Generate constructor stub";
	private static final String TITLE_ADD = "&Add...";
	private static final String TITLE_REMOVE = "&Remove";
	private static final String TITLE_INTERFACES = "Interfaces:";
	private static final String TITLE_EXTENDED_INTERFACES = "Extended interfaces:";
	private static final String TITLE_NAME = "&Name:";
	private static final String TITLE_PREFIX = "&Prefix:";
	private static final String TITLE_SUPERCLASS = "&Superclass:";
	private static final String TITLE_FOLDER = "&Folder:";
	private static final String TITLE_BROWSE = "Browse...";

	public static final int CLASS_TYPE = 1;
	public static final int INTERFACE_TYPE = 2;

	private Composite composite;
	private Text folderText;
	private Text typePrefixText;
	private Text typeText;
	private Text superclassText;
	private PHPElementSelector interfaces;

	private Button[] options;
	private Button[] modifiers;

	private int typeKind;
	private ISelection selection;

	public NewTypeWizardPage(int typeKind, String pageName, ISelection selection) {
		super(pageName);
		
		this.typeKind = typeKind;
		this.selection = selection;
	}

	public void createControl(Composite parent) {
		createDialogSpace(parent);

		buildPage();

		initialize();
		setControl(composite);
	}

	abstract protected void buildPage();

	protected void createDialogSpace(Composite parent) {
		composite = new Composite(parent, SWT.NULL);
		GridLayout layout = new GridLayout();

		composite.setLayout(layout);
		layout.numColumns = 3;
		layout.verticalSpacing = 9;
	}

	protected void addSeparator() {
		LayoutUtils.addSeparator(composite, true, 3);
	}

	protected void createFolderControls() {
		Label label = new Label(composite, SWT.NULL);
		label.setText(TITLE_FOLDER);
		folderText = new Text(composite, SWT.BORDER | SWT.SINGLE);
		LayoutUtils.setGridData(folderText, GridData.FILL, true, GridData.CENTER, false);
		folderText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				handleDialogModify();
			}
		});

		Button button = new Button(composite, SWT.PUSH);
		button.setText(TITLE_BROWSE);
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleBrowseSelection();
			}
		});
	}

	protected void createTypeNameControls() {
		Label label = new Label(composite, SWT.NULL);
		label.setText(TITLE_PREFIX);
		typePrefixText = new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.READ_ONLY);
		LayoutUtils.setGridData(typePrefixText, GridData.FILL, true, GridData.CENTER, false);
		LayoutUtils.addWrapper(composite);

		label = new Label(composite, SWT.NULL);
		label.setText(TITLE_NAME);
		typeText = new Text(composite, SWT.BORDER | SWT.SINGLE);
		LayoutUtils.setGridData(typeText, GridData.FILL, true, GridData.CENTER, false);
		typeText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				handleDialogModify();
			}
		});
		LayoutUtils.addWrapper(composite);
	}

	protected void createSupertypeControls() {
		if (this.typeKind != CLASS_TYPE) {
			return;
		}

		Label label = new Label(composite, SWT.NULL);
		label.setText(TITLE_SUPERCLASS);
		
		superclassText = new Text(composite, SWT.BORDER | SWT.SINGLE);
		LayoutUtils.setGridData(superclassText, GridData.FILL, true, GridData.CENTER, false);
		superclassText.setText("");
		
		Button button = new Button(composite, SWT.PUSH);
		button.setText(TITLE_BROWSE);
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleSupertypeSelect();
			}
		});
	}
	
	protected void createImplementControls() {
		Label label = new Label(composite, SWT.NONE);
		label.setText(this.typeKind == CLASS_TYPE ? TITLE_INTERFACES : TITLE_EXTENDED_INTERFACES);
		LayoutUtils.setGridData(label, GridData.BEGINNING, GridData.BEGINNING, 1, 1, 0, 0);
		
		interfaces = new PHPElementSelector(composite, SWT.BORDER | SWT.FULL_SELECTION);
		LayoutUtils.setGridData(interfaces, GridData.FILL, true, GridData.FILL, true);

		Composite panel = new Composite(composite, SWT.NONE);
		LayoutUtils.setGridData(panel, GridData.FILL, false, GridData.FILL, true);

		RowLayout layout = new RowLayout(SWT.VERTICAL);
		layout.marginLeft = 0;
		layout.marginRight = 0;
		layout.marginTop = 0;
		layout.marginBottom = 0;
		layout.spacing = 3;
		layout.pack = true;
		layout.fill = true;
		panel.setLayout(layout);

		Button button = new Button(panel, SWT.PUSH);
		button.setText(TITLE_ADD);
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleImplementAddSelect();
			}
		});

		label = new Label(panel, SWT.WRAP);

		button = new Button(panel, SWT.PUSH);
		button.setText(TITLE_REMOVE);
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleImplementRemoveSelect();
			}
		});
		panel.pack();
	}
	
	protected void createOptionsControls() {
		if (this.typeKind != CLASS_TYPE) {
			return;
		}
		options = new Button[4];
		
		LayoutUtils.addWrapper(composite);
		options[0] = new Button(composite, SWT.CHECK);
		options[0].setText(TITLE_CONSTRUCTOR);
		options[0].addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleOptionsSelection();
			}
		});
		LayoutUtils.setGridData(options[0], GridData.FILL, GridData.CENTER, 1, 0, 0, 0);
		LayoutUtils.addWrapper(composite);
		createAccessMofidiersControls();
		LayoutUtils.addWrapper(composite);
		
		LayoutUtils.addWrapper(composite);
		options[1] = new Button(composite, SWT.CHECK);
		options[1].setText(TITLE_SUPERCLASS_CONSTRCTOR);
		LayoutUtils.setGridData(options[1], GridData.FILL, GridData.CENTER, 1, 0, 0, 0);
		LayoutUtils.addWrapper(composite);
		
		LayoutUtils.addWrapper(composite);
		options[2] = new Button(composite, SWT.CHECK);
		options[2].setText(TITLE_INHERITED);
		LayoutUtils.addWrapper(composite);
		
		LayoutUtils.addWrapper(composite);
		options[3] = new Button(composite, SWT.CHECK);
		options[3].setText(TITLE_DESTRUCTOR);
		LayoutUtils.addWrapper(composite);
	}
	
	private void createAccessMofidiersControls() {
		LayoutUtils.addWrapper(composite);
		Composite panel = new Composite(composite, SWT.NULL);
		GridLayout layout = new GridLayout(3, true);
		layout.verticalSpacing = 9;
		panel.setLayout(layout);
		LayoutUtils.setGridData(panel, GridData.FILL, GridData.FILL, 2, 2, 0, 0);
		
		modifiers = new Button[5];
		String[] labels1 = {"public", "protected", "private"};
		for (int i = 0; i < labels1.length; i++) {
			modifiers[i] = new Button(panel, SWT.RADIO);
			modifiers[i].setText(labels1[i]);
		}

		String[] labels2 = {"abstract", "final"};
		for (int i = 0; i < labels2.length; i++) {
			int j = labels1.length + i;
			modifiers[j] = new Button(panel, SWT.CHECK);
			modifiers[j].setText(labels2[i]);
		}
	}
	
	private void handleDialogModify() {
		updateStatus(null);
		IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(getFolderName()));
		if (getFolderName().length() == 0) {
			updateStatus("File container must be specified");
			return;
		}
		if (container == null || (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) {
			updateStatus("File container must exist");
			return;
		}
		if (!container.isAccessible()) {
			updateStatus("Project must be writable");
			return;
		}

		if ((container.getType() & IResource.FOLDER) == IResource.FOLDER) {
			String typeNamePrefix = ReflectionUtils.getTypeNamePrefix((IFolder) container);
			if (typeNamePrefix != null) {
				typePrefixText.setText(typeNamePrefix);
			}
		}

		String className = getTypeName();
		if (className.length() == 0) {
			updateStatus("Class name must be specified");
			return;
		}
		if (className.replace('\\', '/').indexOf('/', 1) > 0 || className.indexOf('.') >= 0) {
			updateStatus("Class name must be valid");
			return;
		}
	}

	private void handleBrowseSelection() {
		ContainerSelectionDialog dialog = new ContainerSelectionDialog(
			getShell(), ResourcesPlugin.getWorkspace().getRoot(), false,
			"Select new file container"
		);
		typePrefixText.setText("");
		if (dialog.open() == ContainerSelectionDialog.OK) {
			Object[] result = dialog.getResult();
			if (result.length == 1) {
				folderText.setText(((Path) result[0]).toString());
				typeTextFocus();
			}
		}
	}

	protected void handleSupertypeSelect() {
		OpenPhpClassDialog dialog = new OpenPhpClassDialog(getShell(), typeKind == CLASS_TYPE ? true : false);
		dialog.setFilterText(superclassText.getText());
		if (dialog.open() == ContainerSelectionDialog.OK) {
			PHPClassData parentClass = dialog.getSelectedClass();
			if (parentClass != null) {
				superclassText.setText(parentClass.getName());
			}
		}
	}

	private void handleImplementAddSelect() {
		OpenPhpClassDialog dialog = new OpenPhpClassDialog(getShell(), false);
		if (dialog.open() == ContainerSelectionDialog.OK) {
			PHPClassData phpInterface = dialog.getSelectedClass();
			if (phpInterface != null) {
				interfaces.addRow(phpInterface);
			}
		}
	}

	private void handleImplementRemoveSelect() {
		if (interfaces.getTable().getSelectionCount() == 0) {
			return;
		}

		interfaces.getTable().remove(interfaces.getTable().getSelectionIndices());
	}

	private void handleOptionsSelection() {
		if (options == null || modifiers == null) {
			return;
		}
		options[1].setEnabled(options[0].getSelection());
		for (int i = 0; i < modifiers.length; i++) {
			modifiers[i].setEnabled(options[0].getSelection());
		}
	}

	private void initialize() {
		setDefaultFolder();
		setDefaultTypeName();
		setDefaultOptions();
		handleOptionsSelection();
	}

	protected void setDefaultTypeName() {
		if (typeText == null) {
			return;
		}
		typeText.setText("");
	}

	protected void setDefaultFolder() {
		if (folderText == null) {
			return;
		}
		if (selection != null && selection.isEmpty() == false && selection instanceof IStructuredSelection) {
			IStructuredSelection ssel = (IStructuredSelection) selection;
			if (ssel.size() > 1) {
				return;
			}
			Object obj = ssel.getFirstElement();
			if (obj instanceof IResource) {
				IContainer container;
				if (obj instanceof IContainer) {
					container = (IContainer) obj;
				} else {
					container = ((IResource) obj).getParent();
				}
				folderText.setText(container.getFullPath().toString());
				typeTextFocus();
			}
		}
	}

	protected void setDefaultOptions() {
		if (options == null || modifiers == null) {
			return;
		}
		options[0].setSelection(true);
		options[2].setSelection(true);
		modifiers[0].setSelection(true);
	}

	protected void updateStatus(String message) {
		setErrorMessage(message);
		setPageComplete(message == null);
	}

	public String getFolderName() {
		if (folderText == null) {
			return null;
		}
		return folderText.getText();
	}

	public String getTypeName() {
		if (typeText == null) {
			return null;
		}
		return typeText.getText();
	}

	public String getTypePrefix() {
		if (typePrefixText == null) {
			return null;
		}
		return typePrefixText.getText();
	}

	public String getSupertypeName() {
		if (superclassText == null) {
			return null;
		}
		return superclassText.getText();
	}

	public PHPClassData[] getImplements() {
		if (interfaces == null) {
			return new PHPClassData[0];
		}
		PHPClassData[] result = new PHPClassData[interfaces.getItemCount()];
		for (int i = 0; i < result.length; i++) {
			result[i] = interfaces.getItem(i);
		}
		return result;
	}

	public boolean isConstructor() {
		if (options == null) {
			return false;
		}
		return options[0].getSelection();
	}

	public boolean isConstructorPublic() {
		return isModifierSelected(0);
	}

	public boolean isConstructorProtected() {
		return isModifierSelected(1);
	}

	public boolean isConstructorPrivate() {
		return isModifierSelected(2);
	}

	public boolean isConstructorAbstract() {
		return isModifierSelected(3);
	}

	public boolean isConstructorFinal() {
		return isModifierSelected(4);
	}

	public boolean isSuperclassConstructor() {
		if (options == null) {
			return false;
		}
		return options[1].getSelection();
	}

	public boolean isInherited() {
		if (options == null) {
			return false;
		}
		return options[2].getSelection();
	}

	public boolean isDestructor() {
		if (options == null) {
			return false;
		}
		return options[3].getSelection();
	}

	private boolean isModifierSelected(int modifier) {
		if (modifiers == null || modifiers.length < modifier + 1) {
			return false;
		}
		return modifiers[modifier].getSelection();
	}

	protected void typeTextFocus() {
		typeText.setFocus();
		typeText.setSelection(getTypeName().length());
	}
	
}